package parser;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.debug.BlankDebugEventListener;

import java.io.File;
import java.util.HashMap;
import java.util.Set;

class PackagesParser {
	public long lexerTime = 0;
	public boolean profile = false;
	private HashMap<String,components.Package> packages = new HashMap<String,components.Package>();
	static JavaLexer lexer;
	
	public HashMap<String,components.Package> parse(String[] args) {
		try {
			long start = System.currentTimeMillis();
			if (args.length > 0 ) {
				// for each directory/file specified on the command line
				for(int i=0; i< args.length;i++) {
					doFile(new File(args[i])); // parse it
				}
			}
			else {
				System.err.println("Usage: java Main <directory or file name>");
			}
			
			System.out.println("finished parsing OK");
			if ( profile ) {
				System.out.println("num decisions "+profiler.numDecisions);
			}
		}
		catch(Exception e) {
			System.err.println("exception: "+e);
			e.printStackTrace(System.err);   // so we can get stack trace
		}
		
		return packages;
	}

	// This method decides what action to take based on the type of
	//   file we are looking at
	private void doFile(File f) throws Exception {
		// If this is a directory, walk each file/dir in that directory
		if (f.isDirectory()) {
			String files[] = f.list();
			for(int i=0; i < files.length; i++)
				doFile(new File(f, files[i]));
		}

		// otherwise, if this is a java file, parse it!
		else if ( ((f.getName().length()>5) && f.getName().substring(f.getName().length()-5).equals(".java"))|| f.getName().equals("input") ){
		    System.err.println("parsing "+f.getAbsolutePath());
			parseFile(f.getAbsolutePath());
		}
	}

	class CountDecisions extends BlankDebugEventListener {
		public int numDecisions = 0;
		public void enterDecision(int decisionNumber) {
			numDecisions++;
		}
	}
	CountDecisions profiler = new CountDecisions();

	// Here's where we do the real work...
	private void parseFile(String f) throws Exception {
		try {
			// Create a scanner that reads from the input stream passed to us
			if ( lexer==null ) {
				lexer = new JavaLexer();
			}
			lexer.setCharStream(new ANTLRFileStream(f));
			CommonTokenStream tokens = new CommonTokenStream();
			
			tokens.setTokenSource(lexer);
			long start = System.currentTimeMillis();
			tokens.LT(1); // force load
			long stop = System.currentTimeMillis();
			lexerTime += stop-start;

			// Create a parser that reads from the scanner
			JavaParser parser = null;
			parser = new JavaParser(tokens);
			
			// start parsing at the compilationUnit rule
			parser.compilationUnit();
			
			// add package to the list
			addPackage(parser.getPackage());	
			
		}
		catch (Exception e) {
			System.err.println("parser exception: "+e);
			e.printStackTrace();   // so we can get stack trace		
		}
	}
	
	private void addPackage(components.Package pg){
		String packageName = pg.toString();
		components.Package currentPg = null;
		
		if(packages.containsKey(packageName)){
    		currentPg = packages.get(packageName);
    		for(components.Class c : pg.getClasses())
    			currentPg.addClass(c);
    	}
    	else{
	    	packages.put(packageName,pg);
    	}
	}
	
	public HashMap<String,components.Package> getPackages(){
		return packages;
	}
	
	public void printPackages(){
		for(String pgName : packages.keySet()){
			components.Package pg = packages.get(pgName);
			
			System.out.println("package " + pgName);
			
			for(components.Class _class : pg.getClasses()){
				printClass(_class,0);			
			}	
		}			
	}
	
	private void printClass(components.Class _class, int indentation){
		StringBuilder ind = new StringBuilder("");
		
		for(int i = 0 ; i < indentation ; i++){
			ind.append("    ");
		}
		
		System.out.println(ind + _class.toString());
		Set<String> keys = _class.getMethods().keySet();
		for(String a : keys){
			System.out.println(ind + "    " + a);
		}
		
		Set<String> keysFields = _class.getFields().keySet();
		for(String a : keysFields){
			System.out.println(ind + "    " + a);
		}
		
		for(components.Class c : _class.getInnerClasses()){
			printClass(c,indentation+1);
		}
	}	
}

